package org.lzuploader.controller;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;

import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.HttpVersion;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.lzuploader.model.Localization;
import org.lzuploader.model.message.AbstractMessage;
import org.lzuploader.model.message.ErrorMessage;

/** This class manages connections to the net.
 * It can retrieve any webpage contents and send some data on the
 * net via the POST method.
 * @author Emmanuel Cron
 */
public final class NetConnection
{
    /** The logger for this class. */
    private static Log log = LogFactory.getLog(NetConnection.class);
    
    /** Localization of this class. */
    private static Localization localization = Localization.getInstance();
    
    /** Last message generated by this class. */
    private static AbstractMessage lastMessage;
    
    /**
     * User should never be able to instantiate this class.
     */
    private NetConnection()
    {
        throw new UnsupportedOperationException();
    }
    
    /** 
     * Reads the contents of a page on the net.
     * @param server    The server name
     * @param port      The port on which to connect to
     * @param path      Full path to the page to retrieve (with .html, .php, ...)
     * @return          Returns the contents of the page, or <code>null</code> if no contents could be retrieved
     */
    public static String readPage(final String server, final int port, final String path)
    {
        return processHttpConnection(server, port, path, null);
    }
    
    /** 
     * Sends some contents on a web page via POST method and returns the contents displayed.
     * @param server    The server name
     * @param port      The port on which to connect to
     * @param path      Full path to the web page on which process an operation (with .html, .php, ...)
     * @param parts     Array of {@link Part} objects to be sent
     * @return          Response of the web page on which the operation was performed, or <code>null</code> if no response
     */
    public static String sendToPage(final String server, final int port, final String path, final Part[] parts)
    {
        return processHttpConnection(server, port, path, parts);
    }
    
    /** 
     * Processes a connection on the net using {@link HttpClient}.
     * 
     * @param server    The server name
     * @param port      The port on which to connect to
     * @param path      The path to the page on which process the operation
     * @param parts     An array of {@link Part} objects to be send via POST method on the web page
     * @return          The contents returned by the web page on which process the operation
     */
    private static String processHttpConnection(
        final String server,
        final int port,
        final String path,
        final Part[] parts
    )
    {
        log.debug("Processing connection to the net.");
        
        log.trace("Reseting last message to none.");
        
        lastMessage = null;
        
        log.trace("Creating URL.");
        
        final URL url;
        
        try
        {
            url = new URL("http", server, port, path);
        }
        catch (final MalformedURLException mue)
        {
            log.error("Malformed error detected, returning.");
            
            lastMessage = new ErrorMessage(
                localization.getString("DIALOG_TITLE_ERROR"),
                localization.getString("DIALOG_MALFORMED_URL")
            );
            
            return null;
        }
        
        log.trace("Url computed: " + url.toString());
        
        final HttpClient client = new HttpClient();
        final HttpMethod method;
        
        if (parts == null)
        {
            log.trace("Using GET method.");
            
            method = new GetMethod(url.toString());
        }
        else
        {
            log.trace("Using POST method with some parts.");
            
            final PostMethod post = new PostMethod(url.toString());
            post.setRequestEntity(new MultipartRequestEntity(parts, post.getParams()));
            
            method = post;
        }
        
        log.trace("Setting parameters to method.");
        
        //method.getParams().setSoTimeout(5000);
        method.getParams().setVersion(HttpVersion.HTTP_1_0);
        method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler());
        
        log.trace("Executing method.");
        
        int statusCode;
        
        try
        {
            statusCode = client.executeMethod(method);
        }
        catch (final IOException ioe)
        {
            log.error("IOException while trying to executeMethod(): " + ioe.toString());
            
            lastMessage = new ErrorMessage(
                localization.getString("DIALOG_TITLE_ERROR"),
                localization.getString("DIALOG_COULD_NOT_CONNECT")
            );
            
            method.releaseConnection();
            
            return null;
        }
        
        log.debug("Status code returned: " + statusCode);
        
        String text;
        
        try
        {
            text = method.getResponseBodyAsString();
        }
        catch (final IOException ioe)
        {
            log.error("IOException while trying to getResponseBodyAsString(): " + ioe.toString());
            
            lastMessage = new ErrorMessage(
                localization.getString("DIALOG_TITLE_ERROR"),
                localization.getString("DIALOG_ERROR_WHILE_READING")
            );
            
            return null;
        }
        finally
        {
            log.trace("Releasing connection.");
            
            method.releaseConnection();
        }
        
        log.trace("Text received: " + text);
        
        switch (statusCode)
        {
            case HttpStatus.SC_OK:
                return text;
            case HttpStatus.SC_NOT_FOUND:
                lastMessage = new ErrorMessage(
                    localization.getString("DIALOG_TITLE_ERROR"),
                    localization.getString("DIALOG_NOT_FOUND")
                );
                break;
            case HttpStatus.SC_BAD_REQUEST:
                lastMessage = new ErrorMessage(
                    localization.getString("DIALOG_TITLE_ERROR"),
                    localization.getString("DIALOG_BAD_REQUEST")
                );
                break;
            case HttpStatus.SC_FORBIDDEN:
                lastMessage = new ErrorMessage(
                    localization.getString("DIALOG_TITLE_ERROR"),
                    localization.getString("DIALOG_FORBIDDEN")
                );
                break;
            case HttpStatus.SC_HTTP_VERSION_NOT_SUPPORTED:
                lastMessage = new ErrorMessage(
                    localization.getString("DIALOG_TITLE_ERROR"),
                    localization.getString("DIALOG_HTTP_NOT_SUPPORTED")
                );
                break;
            case HttpStatus.SC_INTERNAL_SERVER_ERROR:
                lastMessage = new ErrorMessage(
                    localization.getString("DIALOG_TITLE_ERROR"),
                    localization.getString("DIALOG_INTERNAL_SERVER_ERROR")
                );
                break;
            case HttpStatus.SC_UNAUTHORIZED:
                lastMessage = new ErrorMessage(
                    localization.getString("DIALOG_TITLE_ERROR"),
                    localization.getString("DIALOG_UNAUTHORIZED")
                );
                break;
            default:
                lastMessage = new ErrorMessage(
                    localization.getString("DIALOG_TITLE_ERROR"),
                    localization.getString("DIALOG_INVALID_CONTENTS")
                );
        }
        
        return null;
    }
    
    /** 
     * Returns wether {@link NetConnection} produced a message on its last process or not.
     * @return <code>true</code> if the last process generated a message, <code>false</code> otherwise.
     */
    public static boolean hasMessage()
    {
        return lastMessage != null;
    }
    
    /** 
     * Returns the last message generated by this class.
     * @return An {@link AbstractMessage} object, or <code>null</code> if none exists.
     */
    public static AbstractMessage getLastMessage()
    {
        return lastMessage;
    }
}
